using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.GamePlay
{
    public class UIN_EquipInfoLevel : UIBaseNode
    {
        #region Template Generate,don't modify
        protected class UIB_UIN_EquipInfoLevel
        {
            #region ObjectBinding Generate
            public IQIGame.Onigao.Framework.ExButton buttonRight { protected set; get; }
            public UnityEngine.GameObject commonEquip { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton buttonLeft { protected set; get; }
            public UnityEngine.GameObject previewLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textPreLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textPreLv2 { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExProgressBar expBar { protected set; get; }
            public UnityEngine.GameObject attributeMould { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnMax { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnClear { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnNext { protected set; get; }
            public IQIGame.Onigao.Framework.ExText textLimitLv { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btnUpdate { protected set; get; }
            public UnityEngine.GameObject costMoney { protected set; get; }
            public UnityEngine.GameObject lvMax { protected set; get; }
            public UnityEngine.GameObject costItem { protected set; get; }
            public UnityEngine.GameObject previewBtnRoot { protected set; get; }
            public virtual void InitBinding(ObjectBinding __binding)
            {
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("buttonRight", out var __tbv0);
                this.buttonRight = __tbv0;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("commonEquip", out var __tbv1);
                this.commonEquip = __tbv1;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("buttonLeft", out var __tbv2);
                this.buttonLeft = __tbv2;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("previewLv", out var __tbv3);
                this.previewLv = __tbv3;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textPreLv", out var __tbv4);
                this.textPreLv = __tbv4;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textPreLv2", out var __tbv5);
                this.textPreLv2 = __tbv5;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textLv", out var __tbv6);
                this.textLv = __tbv6;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExProgressBar>("expBar", out var __tbv7);
                this.expBar = __tbv7;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("attributeMould", out var __tbv8);
                this.attributeMould = __tbv8;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnMax", out var __tbv9);
                this.btnMax = __tbv9;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnClear", out var __tbv10);
                this.btnClear = __tbv10;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnNext", out var __tbv11);
                this.btnNext = __tbv11;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("textLimitLv", out var __tbv12);
                this.textLimitLv = __tbv12;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btnUpdate", out var __tbv13);
                this.btnUpdate = __tbv13;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("costMoney", out var __tbv14);
                this.costMoney = __tbv14;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("lvMax", out var __tbv15);
                this.lvMax = __tbv15;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("costItem", out var __tbv16);
                this.costItem = __tbv16;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("previewBtnRoot", out var __tbv17);
                this.previewBtnRoot = __tbv17;
            }
            #endregion ObjectBinding Generate 
        }
        #endregion Template Generate,don't modify

        #region fields & properties
        UIN_CommonEquip equip = null;
        ListScrollAdapter<UIN_CommonAttribute> attrs = null;
        UIN_CostMoney costMoney = null;
        ListScrollAdapter<UIN_CommonCostItemReduce> costItems = null;

        UD_EquipInfo udEquip = null;
        int equipCount = 0;
        int equipIndex = 0;
        bool isLvChg = false;
        int lvInit = 0;
        int lvCurr = 0;
        int lvMax = 0;
        bool isMaxLvInit = false;
        int expInit = 0;
        int expCurr = 0;
        int expNext = 0;
        float expRateInit = 0;
        int upLvCostMoney = 0;
        int addAllExp = 0;
        Dictionary<int, int> upLvCost = new();
        int cntItemCost = 0;

        protected UIB_UIN_EquipInfoLevel ui { get; set; }
        #endregion

        protected override void BeforeInit()
        {
            ui = new UIB_UIN_EquipInfoLevel();
            ui.InitBinding(this.csObjBind);
        }

        protected override void OnInit()
        {
            this.ui.buttonRight.onClick.AddListener(this.OnClickRight);
            this.ui.buttonLeft.onClick.AddListener(this.OnClickLeft);

            this.ui.btnClear.onClick.AddListener(this.OnClickBtnClear);
            this.ui.btnMax.onClick.AddListener(this.OnClickBtnMaxLv);
            this.ui.btnNext.onClick.AddListener(this.OnClickBtnNextLv);
            this.ui.btnUpdate.onClick.AddListener(this.OnClickUp);

            this.equip = this.InitChildNode<UIN_CommonEquip>(this.ui.commonEquip);
            this.equip.SetCallClickView(this.OnClickEquipView);
            this.equip.SetViewActive(true);

            this.attrs = this.InitListScroll<UIN_CommonAttribute>(this.ui.attributeMould, this.OnAttrChanged);

            this.costMoney = this.InitChildNode<UIN_CostMoney>(this.ui.costMoney);
            this.costItems = this.InitListScroll<UIN_CommonCostItemReduce>(this.ui.costItem, this.OnCostItemChanged);

            this.cntItemCost = APIUIEquip.UpLvCostItems.Count / APIUIEquip.OneCostItemCount;
        }

        protected override void OnShow()
        {

        }

        protected override void OnHide()
        {

        }

        #region 单击
        private void OnClickRight()
        {
            var ui = this.GetBelongUI();
            ui?.ChangeEquip(true);
        }

        private UIEquipInfo GetBelongUI()
        {
            return this.belongWindow as UIEquipInfo;
        }

        private void OnClickLeft()
        {
            var ui = this.GetBelongUI();
            ui?.ChangeEquip(false);
        }

        void OnClickEquipView(int index, UIN_CommonEquip item)
        {
            var ui = this.GetBelongUI();
            ui?.ShowEquipPicture();
        }

        void OnClickBtnClear()
        {
            this.Refresh();
        }

        void OnClickBtnMaxLv()
        {
            if (this.lvCurr == this.lvMax)
                return;
            int diffLv = this.lvMax - this.lvCurr;
            int lv = diffLv > 2 ? this.lvCurr + diffLv / 2 : this.lvCurr + 1;
            int lvLast = lv;
            bool isChged = false;
            while (this.CalcPreview(ref lv))
            {
                isChged = true;
                if (lv < lvLast || lv == this.lvMax)
                    break;
                if (lv > this.lvMax)
                {
                    lv = this.lvMax;
                    this.CalcPreview(ref lv);
                    break;
                }
                diffLv = this.lvMax - lv;
                lv = diffLv > 2 ? lv + diffLv / 2 : lv + 1;
                lvLast = lv;
            }

            if (isChged)
            {
                this.lvCurr = lv;
                this.isLvChg = this.lvCurr != this.lvInit;
                this.ViewUpPreview();
            }
        }

        void OnClickBtnNextLv()
        {
            int lv = Math.Min(this.lvCurr + 1, this.lvMax);
            if (this.lvCurr == lv)
                return;

            if (this.CalcPreview(ref lv))
            {
                this.lvCurr = lv;
                this.isLvChg = this.lvCurr != this.lvInit;
                this.ViewUpPreview();
            }
        }
        void OnClickUp()
        {
            if (this.upLvCost == null || this.upLvCost.Count == 0)
            {
                NoticeModule.ShowNoticeNoCall(APIUIConst.NoticeCid_UpPreNeed);
                return;
            }

            if (!this.costMoney.CheckIsEnough())
            {
                return;
            }

            this.SendUpLv().Forget();
        }

        async UniTaskVoid SendUpLv()
        {
            var equipItem = this.udEquip.equipItem;
            var result = await EquipmentModule.NetCore.CS_upLevel(equipItem.id, this.upLvCost);
            if (result.IsSuccess)
            {
                NoticeModule.ShowUIItemGet(result.returnItems);
                this.Refresh();
            }
        }
        #endregion

        protected override void OnDispose()
        {
            this.equip = null;
            this.attrs = null;
            this.costMoney = null;
            this.costItems = null;

            this.udEquip = null;
            this.equipIndex = 0;
            this.equipCount = 0;
        }

        public void Show(UD_EquipInfo udEquip)
        {
            this.Show();
            this.udEquip = udEquip;
            this.equipCount = udEquip.equipCount;
            this.equipIndex = udEquip.equipIndex;

            this.Refresh();
        }

        void Refresh(ItemEquipmentData equipItem = null)
        {
            equipItem = equipItem == null ? this.udEquip.equipItem : equipItem;
            this.udEquip.ChangeEquip(equipItem);
            this.RefreshData();

            this.equip.Show(equipItem);
            this.ViewBtn();

            this.ViewPreLv();
            this.ViewExpBar(this.expCurr, this.expNext);
            this.ViewLv(this.lvCurr);
            this.ViewLimitLv();
            this.ViewMax();
            this.ViewAttribute();
            this.ViewCostMoney();
            this.ViewCostItem();
        }

        void RefreshData()
        {
            var equipData = this.udEquip.equipItem;
            this.lvInit = equipData.level;
            this.lvCurr = this.lvInit;
            this.lvMax = equipData.maxLv;
            this.isMaxLvInit = this.lvMax <= this.lvInit;
            this.expInit = equipData.exp;
            this.expCurr = this.expInit;
            this.expNext = equipData.cfgLv.NextExp;
            this.expRateInit = this.expNext > 0 ? (float)this.expInit / this.expNext : 0;
            this.isLvChg = false;
            this.upLvCostMoney = 0;
            this.addAllExp = 0;
            this.upLvCost.Clear();
        }

        void ViewBtn()
        {
            this.ui.buttonRight.SetActive(this.equipIndex < this.equipCount - 1);
            this.ui.buttonLeft.SetActive(this.equipIndex > 0);

            this.ui.btnUpdate.SetActive(!this.isMaxLvInit);
            this.ui.previewBtnRoot.SetActive(!this.isMaxLvInit);
        }

        void ViewPreLv()
        {
            this.ui.previewLv.SetActive(this.isLvChg);
            this.ui.textPreLv2.SetActive(this.isLvChg);
            if (this.isLvChg)
            {
                string str = "+" + (this.lvCurr - this.lvInit);
                this.ui.textPreLv.text = str;
                this.ui.textPreLv2.text = str;
            }
        }

        void ViewExpBar(int expCurr, int expNext)
        {
            bool isMaxLv = this.lvCurr >= this.lvMax;
            bool isMaxPreview = isMaxLv && this.lvInit != this.lvCurr;
            int expBar = isMaxPreview ? 0 : this.expInit;
            int expPre = isMaxPreview ? 0 : expCurr;
            this.ui.expBar.SetValue(expBar, expNext).SetPreviewValue(expPre);
            var pbar = this.ui.expBar.progressBar;
            if (pbar != null)
                pbar.SetActive(!this.isLvChg);

            expBar = isMaxLv ? 0 : expCurr;
            expNext = isMaxLv ? 0 : expNext;
            this.ui.expBar.text = APIUIEquip.GetNextExpStr(expBar, expNext);
        }

        void ViewLv(int lv)
        {
            this.ui.textLv.text = lv.ToString();
        }

        void ViewLimitLv()
        {
            this.ui.textLimitLv.text = $"{this.lvInit}/{this.lvMax}";
        }

        void ViewAttribute()
        {
            int cntAttribute = this.udEquip.allAttributeCids.Count;
            this.attrs.RefillCells(cntAttribute);
        }

        void OnAttrChanged(UIN_CommonAttribute item, int index)
        {
            int attrCid = this.udEquip.allAttributeCids[index];
            int attrValue = this.udEquip.GetAttributeValue(attrCid);
            int changNum = !this.isLvChg ? 0 : (this.lvCurr - this.lvInit) * this.udEquip.GetLevelAttributeGrowth(attrCid);
            item.Show(attrCid, attrValue, changNum);
        }

        void ViewMax()
        {
            this.ui.lvMax.SetActive(this.isMaxLvInit);
        }

        void ViewCostMoney()
        {
            if (this.upLvCostMoney > 0)
                this.costMoney.Show(APIUIEquip.UpLvCostMoney[0], this.upLvCostMoney);
            else
                this.costMoney.Hide();
        }

        void ViewCostItem()
        {
            this.costItems.RefillCells(this.cntItemCost);
        }

        void OnCostItemChanged(UIN_CommonCostItemReduce item, int index)
        {
            int indexCid = APIUIEquip.OneCostItemCount * index;
            int cid = APIUIEquip.UpLvCostItems[indexCid];
            int numCost = 0;
            this.upLvCost.TryGetValue(cid, out numCost);
            item.Show(cid, numCost);
            item.SetCallOnReduce(this.OnCostReduce);
        }

        bool CalcPreview(ref int toLv)
        {
            this.addAllExp = 0;
            this.upLvCost.Clear();
            if (this.cntItemCost <= 0)
                return false;

            int needExp = 0;
            for (int i = this.lvInit; i < toLv; i++)
            {
                var cfg = TableCenter.equipLevel.Get(i);
                if (cfg != null)
                    needExp += cfg.NextExp;
            }
            needExp -= this.expInit;
            int kCostStart = 0;

            if (needExp <= 0)
                return false;

            var list = APIUIEquip.UpLvCostItems;
            int costCid = 0;
            int costExp = 0;
            int addAllExp = 0;
            int hasNum = 0;
            List<int> listCostStart = ListPool<int>.Get();
            int kLimit = list.Count - APIUIEquip.OneCostItemCount;
            while (needExp > 0 && kCostStart <= kLimit)
            {
                costCid = list[kCostStart];
                hasNum = ItemModule.Instance.GetNum(costCid);
                if (hasNum > 0)
                {
                    if (!listCostStart.Contains(kCostStart))
                        listCostStart.Add(kCostStart);

                    costExp = list[kCostStart + 1];
                    if (needExp > costExp)
                    {
                        bool isCost = this.upLvCost.TryGetValue(costCid, out var cntUse);
                        if (cntUse < hasNum)
                        {
                            addAllExp += costExp;
                            cntUse++;
                            if (isCost)
                                this.upLvCost[costCid] = cntUse;
                            else
                                this.upLvCost.Add(costCid, cntUse);
                            needExp -= costExp;
                            continue;
                        }
                    }
                }
                kCostStart += APIUIEquip.OneCostItemCount;
            }

            int lastHasCostStart = -1;
            if (listCostStart.Count > 0)
            {
                lastHasCostStart = listCostStart[0];
                listCostStart.Remove(lastHasCostStart);
            }

            while (needExp > 0 && lastHasCostStart >= 0)
            {
                costCid = list[lastHasCostStart];
                hasNum = ItemModule.Instance.GetNum(costCid);
                if (hasNum > 0)
                {
                    bool isCost = this.upLvCost.TryGetValue(costCid, out var cntUse);
                    if (cntUse < hasNum)
                    {
                        costExp = list[lastHasCostStart + 1];
                        addAllExp += costExp;
                        cntUse++;
                        if (isCost)
                            this.upLvCost[costCid] = cntUse;
                        else
                            this.upLvCost.Add(costCid, cntUse);
                        needExp -= costExp;
                    }
                    else if (listCostStart.Count > 0)
                    {
                        lastHasCostStart = listCostStart[0];
                        listCostStart.Remove(lastHasCostStart);
                    }
                    else
                    {
                        lastHasCostStart = -1;
                    }
                }
            }

            ListPool<int>.Put(listCostStart);

            if (needExp <= 0)
            {
                this.expCurr = needExp * -1;
                var toCfg = TableCenter.equipLevel.Get(toLv);
                this.expNext = toCfg?.NextExp ?? 0;
                while (this.expNext > 0 && this.expCurr >= this.expNext && toLv <= this.lvMax)
                {
                    int lv = toLv + 1;
                    if (lv > this.lvMax)
                        break;

                    toLv = lv;
                    this.expCurr -= this.expNext;
                    var cfg = TableCenter.equipLevel.Get(toLv);
                    if (cfg != null)
                        this.expNext = cfg.NextExp;
                }
            }
            else
            {
                int allExp = this.expInit + addAllExp;
                int lvCalc = this.lvInit;
                do
                {
                    var cfg = TableCenter.equipLevel.Get(lvCalc);
                    this.expNext = cfg?.NextExp ?? 0;
                    if (allExp >= this.expNext)
                    {
                        allExp -= this.expNext;
                        lvCalc++;
                    }
                }
                while (this.expNext > 0 && allExp >= this.expNext && lvCalc != toLv);

                toLv = lvCalc;
                this.expCurr = allExp;
            }

            this.addAllExp = addAllExp > 0 ? addAllExp : 0;
            return this.upLvCost.Count > 0;
        }

        void CalcPreviewCostMoney()
        {
            this.upLvCostMoney = 0;
            if (this.addAllExp > 0)
            {
                var cost = APIUIEquip.UpLvCostMoney;
                this.upLvCostMoney = cost[1] * this.addAllExp;
            }
        }

        void ViewUpPreview()
        {
            this.ViewPreLv();
            this.ViewAttribute();
            this.CalcPreviewCostMoney();
            this.ViewCostMoney();
            this.ViewCostItem();
            this.ViewLv(this.lvCurr);
            this.ViewExpBar(this.expCurr, this.expNext);
        }

        void OnCostReduce(int itemCid, int costNum)
        {
            this.upLvCost.TryGetValue(itemCid, out var lastNum);
            if (costNum > 0)
                this.upLvCost[itemCid] = costNum;
            else
                this.upLvCost.Remove(itemCid);

            int numChg = lastNum - costNum;
            if (numChg <= 0)
                return;

            int kCid = 0;
            int expReduce = 0;
            for (var i = 0; i < this.cntItemCost; i++)
            {
                kCid = i * APIUIEquip.OneCostItemCount;
                if (APIUIEquip.UpLvCostItems[kCid] == itemCid)
                {
                    expReduce = APIUIEquip.UpLvCostItems[kCid + 1] * numChg;
                    break;
                }
            }

            this.addAllExp -= expReduce;

            while (expReduce > 0)
            {
                if (this.expCurr >= expReduce)
                {
                    this.expCurr -= expReduce;
                    expReduce = 0;
                    break;
                }
                this.lvCurr--;
                var cfg = TableCenter.equipLevel.Get(this.lvCurr);
                int exp = cfg.NextExp + this.expCurr;
                if (exp < expReduce)
                {
                    expReduce -= exp;
                    this.expCurr = 0;
                }
                else
                {
                    this.expCurr = exp - expReduce;
                    expReduce = 0;
                }
                this.expNext = cfg.NextExp;
            }
            this.isLvChg = this.lvCurr > this.lvInit;
            this.ViewUpPreview();
        }
    }
}
